using System.Net;
using System.Text.Json;
using WeatherSystem.API.Models;
using WeatherSystem.API.Exceptions;

namespace WeatherSystem.API.Middleware
{
    /// <summary>
    /// 全局异常处理中间件
    /// </summary>
    public class GlobalExceptionMiddleware
    {
        private readonly RequestDelegate _next;
        private readonly ILogger<GlobalExceptionMiddleware> _logger;
        private readonly IWebHostEnvironment _environment;

        public GlobalExceptionMiddleware(
            RequestDelegate next,
            ILogger<GlobalExceptionMiddleware> logger,
            IWebHostEnvironment environment)
        {
            _next = next;
            _logger = logger;
            _environment = environment;
        }

        public async Task InvokeAsync(HttpContext context)
        {
            try
            {
                await _next(context);
            }
            catch (Exception ex)
            {
                await HandleExceptionAsync(context, ex);
            }
        }

        private async Task HandleExceptionAsync(HttpContext context, Exception exception)
        {
            _logger.LogError(exception, "处理请求时发生未处理的异常: {RequestPath}", context.Request.Path);

            var response = context.Response;
            response.ContentType = "application/json";

            var errorResponse = new ApiErrorResponse();

            switch (exception)
            {
                case WeatherSystemValidationException validationEx:
                    response.StatusCode = validationEx.HttpStatusCode;
                    errorResponse = new ApiErrorResponse
                    {
                        StatusCode = response.StatusCode,
                        Message = validationEx.Message,
                        Details = validationEx.ErrorCode,
                        Errors = validationEx.ValidationErrors,
                        TraceId = context.TraceIdentifier
                    };
                    break;

                case WeatherSystemException wsEx:
                    response.StatusCode = wsEx.HttpStatusCode;
                    errorResponse = new ApiErrorResponse
                    {
                        StatusCode = response.StatusCode,
                        Message = wsEx.Message,
                        Details = wsEx.ErrorCode,
                        TraceId = context.TraceIdentifier
                    };
                    break;

                case ArgumentNullException argNullEx:
                    response.StatusCode = (int)HttpStatusCode.BadRequest;
                    errorResponse = new ApiErrorResponse
                    {
                        StatusCode = response.StatusCode,
                        Message = "请求参数不能为空",
                        Details = _environment.IsDevelopment() ? argNullEx.Message : null,
                        TraceId = context.TraceIdentifier
                    };
                    break;

                case ArgumentException argEx:
                    response.StatusCode = (int)HttpStatusCode.BadRequest;
                    errorResponse = new ApiErrorResponse
                    {
                        StatusCode = response.StatusCode,
                        Message = "请求参数无效",
                        Details = _environment.IsDevelopment() ? argEx.Message : null,
                        TraceId = context.TraceIdentifier
                    };
                    break;

                case UnauthorizedAccessException:
                    response.StatusCode = (int)HttpStatusCode.Unauthorized;
                    errorResponse = new ApiErrorResponse
                    {
                        StatusCode = response.StatusCode,
                        Message = "未授权访问",
                        TraceId = context.TraceIdentifier
                    };
                    break;

                case KeyNotFoundException notFoundEx:
                    response.StatusCode = (int)HttpStatusCode.NotFound;
                    errorResponse = new ApiErrorResponse
                    {
                        StatusCode = response.StatusCode,
                        Message = "请求的资源不存在",
                        Details = _environment.IsDevelopment() ? notFoundEx.Message : null,
                        TraceId = context.TraceIdentifier
                    };
                    break;

                case InvalidOperationException invalidOpEx:
                    response.StatusCode = (int)HttpStatusCode.BadRequest;
                    errorResponse = new ApiErrorResponse
                    {
                        StatusCode = response.StatusCode,
                        Message = "操作无效",
                        Details = _environment.IsDevelopment() ? invalidOpEx.Message : null,
                        TraceId = context.TraceIdentifier
                    };
                    break;

                case TimeoutException timeoutEx:
                    response.StatusCode = (int)HttpStatusCode.RequestTimeout;
                    errorResponse = new ApiErrorResponse
                    {
                        StatusCode = response.StatusCode,
                        Message = "请求超时",
                        Details = _environment.IsDevelopment() ? timeoutEx.Message : null,
                        TraceId = context.TraceIdentifier
                    };
                    break;

                case TaskCanceledException:
                    response.StatusCode = (int)HttpStatusCode.RequestTimeout;
                    errorResponse = new ApiErrorResponse
                    {
                        StatusCode = response.StatusCode,
                        Message = "请求被取消",
                        TraceId = context.TraceIdentifier
                    };
                    break;

                case NotImplementedException notImplEx:
                    response.StatusCode = (int)HttpStatusCode.NotImplemented;
                    errorResponse = new ApiErrorResponse
                    {
                        StatusCode = response.StatusCode,
                        Message = "功能未实现",
                        Details = _environment.IsDevelopment() ? notImplEx.Message : null,
                        TraceId = context.TraceIdentifier
                    };
                    break;

                case BusinessLogicException businessEx:
                    response.StatusCode = (int)HttpStatusCode.BadRequest;
                    errorResponse = new ApiErrorResponse
                    {
                        StatusCode = response.StatusCode,
                        Message = businessEx.Message,
                        Details = businessEx.Details,
                        TraceId = context.TraceIdentifier
                    };
                    break;

                case ValidationException validationEx:
                    response.StatusCode = (int)HttpStatusCode.BadRequest;
                    errorResponse = new ApiErrorResponse
                    {
                        StatusCode = response.StatusCode,
                        Message = "数据验证失败",
                        Details = validationEx.Message,
                        Errors = validationEx.Errors?.ToDictionary(x => x.PropertyName, x => new[] { x.ErrorMessage }),
                        TraceId = context.TraceIdentifier
                    };
                    break;

                case HttpRequestException httpEx:
                    response.StatusCode = (int)HttpStatusCode.BadGateway;
                    errorResponse = new ApiErrorResponse
                    {
                        StatusCode = response.StatusCode,
                        Message = "外部服务调用失败",
                        Details = _environment.IsDevelopment() ? httpEx.Message : null,
                        TraceId = context.TraceIdentifier
                    };
                    break;

                default:
                    response.StatusCode = (int)HttpStatusCode.InternalServerError;
                    errorResponse = new ApiErrorResponse
                    {
                        StatusCode = response.StatusCode,
                        Message = "服务器内部错误",
                        Details = _environment.IsDevelopment() ? exception.Message : null,
                        TraceId = context.TraceIdentifier
                    };

                    // 记录未知异常的完整堆栈跟踪
                    _logger.LogError(exception, "未处理的内部服务器错误");
                    break;
            }

            // 添加响应头
            response.Headers.Add("X-Error-Source", "GlobalExceptionMiddleware");
            response.Headers.Add("X-Timestamp", DateTimeOffset.UtcNow.ToString("O"));

            var jsonResponse = JsonSerializer.Serialize(errorResponse, new JsonSerializerOptions
            {
                PropertyNamingPolicy = JsonNamingPolicy.CamelCase,
                WriteIndented = _environment.IsDevelopment()
            });

            await response.WriteAsync(jsonResponse);
        }
    }

    /// <summary>
    /// API错误响应模型
    /// </summary>
    public class ApiErrorResponse
    {
        public int StatusCode { get; set; }
        public string Message { get; set; } = "";
        public string? Details { get; set; }
        public string? TraceId { get; set; }
        public DateTime Timestamp { get; set; } = DateTime.UtcNow;
        public Dictionary<string, string[]>? Errors { get; set; }
    }

    /// <summary>
    /// 业务逻辑异常
    /// </summary>
    public class BusinessLogicException : Exception
    {
        public string? Details { get; }

        public BusinessLogicException(string message) : base(message)
        {
        }

        public BusinessLogicException(string message, string details) : base(message)
        {
            Details = details;
        }

        public BusinessLogicException(string message, Exception innerException) : base(message, innerException)
        {
        }
    }

    /// <summary>
    /// 验证异常
    /// </summary>
    public class ValidationException : Exception
    {
        public IEnumerable<ValidationError> Errors { get; }

        public ValidationException(string message) : base(message)
        {
            Errors = Enumerable.Empty<ValidationError>();
        }

        public ValidationException(string message, IEnumerable<ValidationError> errors) : base(message)
        {
            Errors = errors ?? Enumerable.Empty<ValidationError>();
        }
    }

    public class ValidationError
    {
        public string PropertyName { get; set; } = "";
        public string ErrorMessage { get; set; } = "";
    }
}